/*
 * BehaviorAutomata.c
 * Behavioral Attributed Automaton
 * @author Gregoire JACOB (gregoire.jacob@orange-ftgroup.com)
 * @date 01/12/2008
 * @version 2.2
 * Detection by parallel attributed automata
 */

#include ".\Includes\BehaviorAutomata.h"
#include <time.h>


unsigned long getEvent(FILE * log, long * arg1id, int * arg1type,
					   long * arg2id, int * arg2type){
	unsigned long readevent, arg;
	size_t res;
	//Read event
	res = fread(&readevent,sizeof(long),1,log);
	if(res!=1) return -1;
	//if(readevent&0xFFFFFF00){
	//	printf("[+] %s ",eventstr[(readevent-1)&0x0000000F]);
	//}else{
	//	printf("[+] %s ",limitstr[readevent]);
	//}
	//Read optional paratemers
	if(readevent&OP_ARG){
		res = fread(&arg,sizeof(long),1,log); if(res!=1) return -1;
		*arg1type = (int)(arg>>16);
		res = fread(&arg,sizeof(long),1,log); if(res!=1) return -1;
		*arg1id = arg;
		//printf("%s%d",typestr[*arg1type],*arg1id);
	}
	if(readevent&OP_ARG2){
		res = fread(&arg,sizeof(long),1,log); if(res!=1) return -1;
		*arg2type = (int)(arg>>16);
		res = fread(&arg,sizeof(long),1,log); if(res!=1) return -1;
		*arg2id = arg;
		//printf(", %s%d",typestr[*arg2type],*arg2id);
	}
	//printf("\n");
	nbevent++;
	return readevent;
}

void writeXMLPreamble(FILE * db){
	fprintf(db,"<?xml version=\"1.0\"?>\n");
	fprintf(db,"<!DOCTYPE Behaviors [\n");
	fprintf(db,"\t<!ELEMENT Behaviors (Duplication|Propagation|Residency|Overinfection|ExecutionProxy)*>\n");
	fprintf(db,"\t<!ELEMENT Duplication (sequence,flow,source,target,transit?)>\n");
	fprintf(db,"\t<!ELEMENT Propagation (sequence,flow,source,interface,transit?)>\n");
	fprintf(db,"\t<!ELEMENT Residency (sequence,value,target)>\n");
	fprintf(db,"\t<!ELEMENT Overinfection (sequence,conditional,marker)>\n");
	fprintf(db,"\t<!ELEMENT ExecutionProxy (sequence,interface,target)>\n");
	fprintf(db,"\t<!ELEMENT sequence EMPTY>\n");
	fprintf(db,"\t<!ATTLIST sequence number ID #REQUIRED>\n");
	fprintf(db,"\t<!ELEMENT flow EMPTY>\n");
	fprintf(db,"\t<!ATTLIST flow method (transfer|single-block|interleaved) #REQUIRED>\n");
	fprintf(db,"\t<!ELEMENT conditionnal EMPTY>\n");
	fprintf(db,"\t<!ATTLIST conditionnal method (straight|inverse) #REQUIRED>\n");  
	fprintf(db,"\t<!ELEMENT source EMPTY>\n");
	fprintf(db,"\t<!ATTLIST source id CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST source name CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST source nature (none|file|folder|drive|registry|network|mail|variable) #REQUIRED>\n");
	fprintf(db,"\t<!ELEMENT target EMPTY>");
	fprintf(db,"\t<!ATTLIST target id CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST target name CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST target nature (none|file|folder|drive|registry|network|mail|variable) #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST target status (created|existing) #REQUIRED>\n"); 
	fprintf(db,"\t<!ELEMENT interface EMPTY>\n");
	fprintf(db,"\t<!ATTLIST interface id CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST interface name CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST interface nature (none|file|folder|drive|registry|network|mail|variable) #REQUIRED>\n");
	fprintf(db,"\t<!ELEMENT transit EMPTY>\n");
	fprintf(db,"\t<!ATTLIST transit id CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST transit nature (none|file|folder|drive|registry|network|mail|variable) #REQUIRED>\n");
	fprintf(db,"\t<!ELEMENT value EMPTY>\n");
	fprintf(db,"\t<!ATTLIST value id CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST value nature (none|file|folder|drive|registry|network|mail|variable) #REQUIRED>\n");
	fprintf(db,"\t<!ELEMENT marker EMPTY>\n");
	fprintf(db,"\t<!ATTLIST marker id CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST marker name CDATA #REQUIRED>\n");
	fprintf(db,"\t<!ATTLIST marker nature (none|file|folder|drive|registry|network|mail|variable) #REQUIRED>\n");
	fprintf(db,"]>\n\n\n");
}

void storeBehaviorEntry(FILE * db, int behref, int method, 
						long obj1id, long obj2id, long obj3id){
	int i, strlgth;
	static unsigned int nbdupentry = 1;
	static unsigned int nbpropentry = 1;
	static unsigned int nbresentry = 1;
	static unsigned int nbovinfentry = 1;
	static unsigned int nbexeproxentry = 1;

	static char behaviors[5][16] = {"Duplication","Propagation",
									"Residency","Overinfection","ExecutionProxy"};
	static char duppropmethods[3][15] = {"transfer","single-block","interleaved"};
	static char ovinfmethods[2][25] = {"straight","inverse"};
	static char status[2][10] = {"created","existing"}; 
	static char nature[8][15] = {"none","file","folder","drive",
								 "registry","network","mail","variable"}; 

	fprintf(db,"<%s>\n",behaviors[behref]);
	//Normalizing names for XML (blanks, unprintable characters) 
	switch(behref){
		case DUPLICATION_REF:
		case PROPAGATION_REF:
		case EXECUTIONPROXY_REF:
			if(ps_ObjDB[obj1id].ObjectName==NULL){
				ps_ObjDB[obj1id].ObjectName = (char*)calloc(NAME_MAX_LENGTH,sizeof(char));
				strcpy_s(ps_ObjDB[obj1id].ObjectName,NAME_MAX_LENGTH,
						 "\"Information lost during collection\"");}
			strlgth = (int)strlen(ps_ObjDB[obj1id].ObjectName);
			for(i=0;i<strlgth-1;i++){//Remove unprintable characters
				if(ps_ObjDB[obj1id].ObjectName[i]<32) ps_ObjDB[obj1id].ObjectName[i]='#';
				if(ps_ObjDB[obj1id].ObjectName[i]>126) ps_ObjDB[obj1id].ObjectName[i]='#';
			}
			ps_ObjDB[obj1id].ObjectName[strlgth-1] = '"';
			if(ps_ObjDB[obj2id].ObjectName==NULL){
				ps_ObjDB[obj2id].ObjectName = (char*)calloc(NAME_MAX_LENGTH,sizeof(char));
				strcpy_s(ps_ObjDB[obj2id].ObjectName,NAME_MAX_LENGTH,
						 "\"Information lost during collection\"");}
			strlgth = (int)strlen(ps_ObjDB[obj2id].ObjectName);
			for(i=0;i<strlgth-1;i++){//Remove unprintable characters
				if(ps_ObjDB[obj2id].ObjectName[i]<32) ps_ObjDB[obj2id].ObjectName[i]='#';
				if(ps_ObjDB[obj2id].ObjectName[i]>126) ps_ObjDB[obj2id].ObjectName[i]='#';
			}
			ps_ObjDB[obj2id].ObjectName[strlgth-1] = '"';
			break;
		case RESIDENCY_REF:
			if(ps_ObjDB[obj2id].ObjectName==NULL){
				ps_ObjDB[obj2id].ObjectName = (char*)calloc(NAME_MAX_LENGTH,sizeof(char));
				strcpy_s(ps_ObjDB[obj2id].ObjectName,NAME_MAX_LENGTH,
						 "\"Information lost during collection\"");}
			strlgth = (int)strlen(ps_ObjDB[obj2id].ObjectName);
			for(i=0;i<strlgth-1;i++){//Remove unprintable characters
				if(ps_ObjDB[obj2id].ObjectName[i]<32) ps_ObjDB[obj2id].ObjectName[i]='#';
				if(ps_ObjDB[obj2id].ObjectName[i]>126) ps_ObjDB[obj2id].ObjectName[i]='#';
			}
			ps_ObjDB[obj2id].ObjectName[strlgth-1] = '"';
			break;
		case OVERINFECTION_REF:
			if(ps_ObjDB[obj1id].ObjectName==NULL){
				ps_ObjDB[obj1id].ObjectName = (char*)calloc(NAME_MAX_LENGTH,sizeof(char));
				strcpy_s(ps_ObjDB[obj1id].ObjectName,NAME_MAX_LENGTH,
						 "\"Information lost during collection\"");}
			strlgth = (int)strlen(ps_ObjDB[obj1id].ObjectName);
			for(i=0;i<strlgth-1;i++){//Remove unprintable characters
				if(ps_ObjDB[obj1id].ObjectName[i]<32) ps_ObjDB[obj1id].ObjectName[i]='#';
				if(ps_ObjDB[obj1id].ObjectName[i]>126) ps_ObjDB[obj1id].ObjectName[i]='#';
			}
			ps_ObjDB[obj1id].ObjectName[strlgth-1] = '"';
			break;
	};

	
	//Store information
	switch(behref){
		case DUPLICATION_REF:
			fprintf(db,"\t<sequence number=\"%d\"/>\n",nbdupentry);
			fprintf(db,"\t<flow method=\"%s\"/>\n",duppropmethods[method]);
			fprintf(db,"\t<source id=\"%d\" name=%s nature=\"%s\"/>\n",
					obj1id,ps_ObjDB[obj1id].ObjectName,
					nature[ps_ObjDB[obj1id].Nature]);
			fprintf(db,"\t<target id=\"%d\" name=%s nature=\"%s\" status=\"%s\"/>\n",
					obj2id,ps_ObjDB[obj2id].ObjectName,
					nature[ps_ObjDB[obj2id].Nature],
					status[ps_ObjDB[obj2id].Status]);
			if(method) fprintf(db,"\t<transit id=\"%d\" nature=\"%s\"/>\n",
					obj3id,nature[ps_ObjDB[obj3id].Nature]);
			nbdupentry++;
			break;
		case PROPAGATION_REF:
			fprintf(db,"\t<sequence number=\"%d\"/>\n",nbpropentry);
			fprintf(db,"\t<flow method=\"%s\"/>\n",duppropmethods[method]);
			fprintf(db,"\t<source id=\"%d\" name=%s nature=\"%s\"/>\n",
					obj1id,ps_ObjDB[obj1id].ObjectName,
					nature[ps_ObjDB[obj1id].Nature]);
			fprintf(db,"\t<interface id=\"%d\" name=%s nature=\"%s\"/>\n",
					obj2id,ps_ObjDB[obj2id].ObjectName,
					nature[ps_ObjDB[obj2id].Nature]);
			if(method) fprintf(db,"\t<transit id=\"%d\" nature=\"%s\"/>\n",
					obj3id,nature[ps_ObjDB[obj3id].Nature]);
			nbpropentry++;
			break;
		case RESIDENCY_REF:
			fprintf(db,"\t<sequence number=\"%d\"/>\n",nbresentry);
			fprintf(db,"\t<value id=\"%d\" nature=\"%s\"/>\n",
					obj1id,nature[ps_ObjDB[obj1id].Nature]);
			fprintf(db,"\t<target id=\"%d\" name=%s nature=\"%s\" status=\"%s\"/>\n",
					obj2id,ps_ObjDB[obj2id].ObjectName,
					nature[ps_ObjDB[obj2id].Nature],
					status[ps_ObjDB[obj2id].Status]);
			nbresentry++;
			break;
		case OVERINFECTION_REF:
			fprintf(db,"\t<sequence number=\"%d\"/>\n",nbovinfentry);
			fprintf(db,"\t<conditional method=\"%s\"/>\n",ovinfmethods[method]);
			fprintf(db,"\t<marker id=\"%d\" name=%s nature=\"%s\"/>\n",
					obj1id,ps_ObjDB[obj1id].ObjectName,
					nature[ps_ObjDB[obj1id].Nature]);
			nbovinfentry++;
			break;
		case EXECUTIONPROXY_REF:
			fprintf(db,"\t<sequence number=\"%d\"/>\n",nbexeproxentry);
			fprintf(db,"\t<interface id=\"%d\" name=%s nature=\"%s\"/>\n",
					obj1id,ps_ObjDB[obj1id].ObjectName,
					nature[ps_ObjDB[obj1id].Nature]);
			fprintf(db,"\t<target id=\"%d\" name=%s nature=\"%s\" status=\"%s\"/>\n",
					obj2id,ps_ObjDB[obj2id].ObjectName,
					nature[ps_ObjDB[obj2id].Nature],
					status[ps_ObjDB[obj2id].Status]);
			nbexeproxentry++;
			break;
	};
	fprintf(db,"</%s>\n\n",behaviors[behref]);
	//Maximum of detected behavior, more are no longer necessary
	if((nbdupentry+nbpropentry+nbresentry+nbovinfentry)>1000){
		fprintf(db,"</Behaviors>\n");
		fclose(db);
		printf("[+]Maximum number of behaviors reached: detection ended\n");
		exit(0);
	}
}



/**
 * main()
 * Open the log and launch detection
 */
int __cdecl main( int argc, char ** argv ){
	char odbname[NAME_MAX_LENGTH];
	FILE * pf_Log = NULL;
	FILE * pf_Odbf = NULL;
	long arg1id, arg2id;
	int arg1type, arg2type, i, lgth;
	unsigned long ev; errno_t err;
	clock_t t1, t2; float t;
	pf_BehDB = NULL;
	
	if(argc != 3){
		printf("[-] Incorrect parameters, correct usage is:\n");
		printf("      BehaviorAutomata [Log file] [behavior database file]\n");
		printf("      Log file in binary\n");
	}else{
		//Opening the log file
		err = fopen_s(&pf_Log,argv[1],"rb");
		if(err){
			printf("[-] Log file can not be opened\n");
			exit(0);
		}//if
		//Opening the related object database
		lgth = (int)strlen(argv[1]);
		strcpy_s(odbname,NAME_MAX_LENGTH,argv[1]);
		for(i=lgth-4;i<lgth;i++){
			if(odbname[i]=='.'){odbname[i]=0; break;}
		}
		strcat_s(odbname,NAME_MAX_LENGTH,".odb");
		err = fopen_s(&pf_Odbf,odbname,"rb");
		if(err){
			printf("[-] Related object database file can not be opened\n");
			exit(0);
		}
		//Loading object database
		loadObjectDatabase(pf_Odbf);

		//Opening the behavior database file
		err = fopen_s(&pf_BehDB,argv[2],"wb");
		if(err){
			printf("[-] Behavior database file can not be opened\n");
			exit(0);
		}//if

		//Performance
		t1 = clock();
		nbevent = 0;
		nbambiguous = 0;
		//Initialize behavior monitors
		initDuplicationAutomata();
		initPropagationAutomata();
		initResidencyAutomata();
		initOverinfectionAutomata();
		initActivityAutomata();
		initExecutionAutomata();
		printf("[+] Start parsing the file\n");
		writeXMLPreamble(pf_BehDB);
		fprintf(pf_BehDB,"<Behaviors>\n");
		arg1id = -1; arg1type = -1; arg2id = -1; arg2type = -1; 
		ev = getEvent(pf_Log,&arg1id,&arg1type,&arg2id,&arg2type);
		while(ev!=-1){
			//Monitoring the behaviors
			updateDuplicationAutomata(ev,arg1id,arg1type,arg2id,arg2type);
			updatePropagationAutomata(ev,arg1id,arg1type,arg2id,arg2type);
			updateResidencyAutomata(ev,arg1id,arg1type,arg2id,arg2type);
			updateOverinfectionAutomata(ev,arg1id,arg1type,arg2id,arg2type);
			updateActivityAutomata(ev,arg1id,arg1type,arg2id,arg2type);
			updateExecutionAutomata(ev,arg1id,arg1type,arg2id,arg2type);
			//Read next event
			arg1id = -1; arg1type = -1; arg2id = -1; arg2type = -1; 
			ev = getEvent(pf_Log,&arg1id,&arg1type,&arg2id,&arg2type);
			//printf("Automata: -duplication:%d\n",duplication.nbnautomata);
			//printf("Automata -executionproxy:%d\n",execution.nbnautomata);
		}
		fprintf(pf_BehDB,"</Behaviors>\n");
		fclose(pf_BehDB);
		fclose(pf_Log);
		//Performance
		t2 = clock();
		t = (float)t2-t1;
		t = t/CLOCKS_PER_SEC;
		printf("Execution time: %f sec\n",t);
		printf("Events: %d | Ambiguities raised: %d\n",nbevent,nbambiguous);
		printf("Automata: -duplication:%d\n",duplication.nbnautomata);
		printf("          -propagation:%d\n",propagation.nbnautomata);
		printf("          -residency:%d\n",residency.nbnautomata);
		printf("          -overinfection:%d\n",overinfection.nbnautomata);
		printf("          -executionproxy:%d\n",execution.nbnautomata);
		
	}

	return 0;
}
